package com.hpq.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.hpq.chat.service.RoomService;
import com.hpq.common.annotation.RedissonLock;
import com.hpq.common.domain.vo.request.CursorPageBaseReq;
import com.hpq.common.domain.vo.request.PageBaseReq;
import com.hpq.common.domain.vo.response.CursorPageBaseResp;
import com.hpq.common.domain.vo.response.PageBaseResp;
import com.hpq.common.event.FriendApplyEvent;
import com.hpq.common.utils.AssertUtil;
import com.hpq.user.dao.UserApplyDAO;
import com.hpq.user.dao.UserDAO;
import com.hpq.user.dao.UserFriendDAO;
import com.hpq.user.domain.model.User;
import com.hpq.user.domain.model.UserApply;
import com.hpq.user.domain.model.UserFriend;
import com.hpq.user.domain.vo.request.friend.FriendApplyReq;
import com.hpq.user.domain.vo.request.friend.FriendCheckReq;
import com.hpq.user.domain.vo.request.friend.FriendDeleteReq;
import com.hpq.user.domain.vo.response.friend.FriendApplyResp;
import com.hpq.user.domain.vo.response.friend.FriendCheckResp;
import com.hpq.user.domain.vo.response.friend.FriendResp;
import com.hpq.user.service.FriendService;
import com.hpq.user.service.adapter.FriendAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *  @author hpq
 *  好友接口实现类
 */
@Slf4j
@Service
public class FriendServiceImpl implements FriendService {
    @Autowired
    private UserFriendDAO userFriendDao;
    @Autowired
    private UserDAO userDao;
    @Autowired
    private UserApplyDAO  userApplyDao;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private RoomService roomService;
    /**
     * 获取好友列表
     * @param uid
     * @param req
     * @return
     */
    @Override
    public CursorPageBaseResp<FriendResp> getFriendList(Long uid, CursorPageBaseReq req) {
        CursorPageBaseResp<UserFriend> cursorPage = userFriendDao.getFriendPage(uid, req);
        if(CollectionUtils.isEmpty(cursorPage.getData())){
            return CursorPageBaseResp.empty();
        }
        List<Long> friendUids = cursorPage.getData()
                .stream()
                .map(UserFriend::getFriendUid)
                .collect(Collectors.toList());
        List<User> userList = userDao.getFriendList(friendUids);
        return CursorPageBaseResp.init(cursorPage, FriendAdapter.buildFriendResp(cursorPage.getData(),userList));
    }

    /**
     *  校验好友
     * @param uid
     * @param req
     * @return
     */
    @Override
    public FriendCheckResp check(Long uid, FriendCheckReq req) {
        List<UserFriend> friendList = userFriendDao.getFriendByUids(uid, req.getUidList());
        Set<Long> friendUids = friendList.stream().map(UserFriend::getFriendUid).collect(Collectors.toSet());
        List<FriendCheckResp.FriendCheck> friendCheckList = req.getUidList().stream().map(friend -> {
            FriendCheckResp.FriendCheck friendCheck = new FriendCheckResp.FriendCheck();
            friendCheck.setUid(friend);
            friendCheck.setIsFriend(friendUids.contains(friend));
            return friendCheck;
        }).collect(Collectors.toList());
        return new FriendCheckResp(friendCheckList);
    }

    /**
     *  申请好友
     * @param uid
     * @param req
     */
    @Override
    @RedissonLock(key = "#uid")
    public void apply(Long uid, FriendApplyReq req) {
        //是否已经有这个好友
        UserFriend friend = userFriendDao.getFriendByUid(uid, req.getTargetUid());
        AssertUtil.isNotEmpty(friend, "好友已存在");
        //判断是否有待审批的申请记录(自己申请的）
        UserApply selfUserApply = userApplyDao.getApproving(uid, req.getTargetUid());
        if(Objects.nonNull(selfUserApply)){
            log.info("已经有好友申请及记录了: uid: {},targetId: {}",uid, req.getTargetUid());
            return;
        }
        //判断是否有待审批的申请记录(别人申请的）
        UserApply targetUserApply = userApplyDao.getApproving(req.getTargetUid(), uid);
        if(Objects.nonNull(targetUserApply)){
            log.info("已经有好友申请及记录了: uid: {},targetId: {}",req.getTargetUid(), uid);
            return;
        }
        UserApply userApply = FriendAdapter.buildFriendApply(uid, req);
        //保存申请记录
        userApplyDao.save(userApply);
        //申请事件
        applicationEventPublisher.publishEvent(new FriendApplyEvent(this,userApply));
        log.info("申请好友请求发送成功: uid: {},targetId: {}",uid, req.getTargetUid());
    }

    /**
     *  删除好友
     * @param uid
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFriend(Long uid, FriendDeleteReq req) {
        //双向删除，查找到两条好友记录
        List<UserFriend> userFriends = userFriendDao.getUserFriends(uid, req.getTargetUid());
        if(CollectionUtils.isEmpty(userFriends)){
            log.info("不是好友关系，无法删除: uid: {},targetId: {}",uid, req.getTargetUid());
            return;
        }
        List<Long> userFriendIds = userFriends.stream().map(UserFriend::getId).collect(Collectors.toList());
        //删除好友记录
        userFriendDao.removeByIds(userFriendIds);
        //禁用房间
        roomService.disableFriendRoom(Arrays.asList(uid, req.getTargetUid()));
    }

    /**
     *  获取好友申请列表
     * @param uid
     * @param req
     * @return
     */
    @Override
    public PageBaseResp<FriendApplyResp> getFriendApplyByPage(Long uid, PageBaseReq req) {
       IPage<UserApply> userApplyIPage =  userApplyDao.friendApplyPage(uid, req);
        if(CollectionUtil.isEmpty(userApplyIPage.getRecords())){
            return PageBaseResp.empty();
        }
        //消息设为已读
        readApplies(uid ,userApplyIPage);
        return PageBaseResp.init(userApplyIPage, FriendAdapter.buildFriendApplyResp(userApplyIPage.getRecords()));
    }

    /**
     *  好友申请消息设为已读
     * @param uid
     * @param userApplyIPage
     */
    private void readApplies(Long uid,IPage<UserApply> userApplyIPage) {
        List<Long> applyIds = userApplyIPage.getRecords()
                .stream()
                .map(UserApply::getId)
                .collect(Collectors.toList());
        userApplyDao.readApplies(uid,applyIds);
    }
}
